// <copyright file="BinarySchema.cs" company="Largo"> // Copyright (c) 2009 All Right Reserved // </copyright> // <author> vl </author> // <email></email> // <date>2009-01-01</date> // <summary>Contains ...</summary> namespace LargoCommon.Music { using System; using System.Collections; using System.Collections.ObjectModel; using System.Diagnostics.Contracts; using System.Text; using System.Xml.Serialization; using Abstract; /// <summary> /// Binary Schema. /// </summary> [Serializable] [XmlRoot] public class BinarySchema : BinaryStructure { #region Fields /// <summary> List of places. </summary> private Collection<byte> places; /// <summary> List of distances. </summary> private Collection<byte> distances; #endregion #region Constructors /// <summary> Initializes a new instance of the BinarySchema class. </summary> public BinarySchema() //// resharper - redundant call : base() { } /// <summary> /// Initializes a new instance of the BinarySchema class. /// </summary> /// <param name="givenSystem">The given system.</param> /// <param name="structuralCode">Structural code.</param> public BinarySchema(GeneralSystem givenSystem, string structuralCode) : base(givenSystem, structuralCode) { Contract.Requires(givenSystem != null); this.FormalBehavior = new FormalBehavior(); this.RhythmicBehavior = new RhythmicBehavior(); } /// <summary> /// Initializes a new instance of the BinarySchema class. /// </summary> /// <param name="givenSystem">The given system.</param> /// <param name="givenBitArray">Bit array.</param> public BinarySchema(GeneralSystem givenSystem, BitArray givenBitArray) : base(givenSystem, givenBitArray) { Contract.Requires(givenSystem != null); this.FormalBehavior = new FormalBehavior(); this.RhythmicBehavior = new RhythmicBehavior(); } /// <summary> /// Initializes a new instance of the BinarySchema class. /// </summary> /// <param name="givenSystem">The given system.</param> /// <param name="number">Number of structure.</param> public BinarySchema(GeneralSystem givenSystem, long number) : base(givenSystem, number) { this.FormalBehavior = new FormalBehavior(); this.RhythmicBehavior = new RhythmicBehavior(); } /// <summary> Initializes a new instance of the BinarySchema class. </summary> /// <param name="structure">Binary structure.</param> public BinarySchema(BinaryStructure structure) : base(structure) { Contract.Requires(structure != null); this.FormalBehavior = new FormalBehavior(); this.RhythmicBehavior = new RhythmicBehavior(); } #endregion #region Properties /// <summary> Gets order of system. </summary> /// <value> Property description. </value> public byte Order => this.GSystem.Order; /// <summary> Gets schema of positions. </summary> /// <value> Property description. </value> public string PosSchema => this.PlaceString(); /// <summary> Gets binary schema of elements. </summary> /// <value> Property description. </value> [XmlAttribute] public virtual string ElementSchema { get { var s = new StringBuilder(); for (byte e = 0; e < this.GSystem.Order; e++) { s.Append(this.IsOn(e) ? '1' : '0'); } //// string se = (from e in Enumerable.Range(0, this.GSystem.Order).Cast<Byte>() //// select this.IsOn(e) ? "1":"0") .Aggregate((current, next) => current + ", " + next); */ return s.ToString().Trim(); } } #endregion #region Places and distances /// <summary> Gets positions of nonzero bits. </summary> /// <value> Property description. </value> [XmlIgnore] public Collection<byte> Places { get { Contract.Ensures(Contract.Result<Collection<byte>>() != null); if (this.places == null) { this.places = this.BitPlaces; } //// if (this.places == null) { //// throw new InvalidOperationException("List of places is null."); } return new Collection<byte>(this.places); } } /// <summary> Gets distances of nonzero bits. </summary> /// <value> Property description. </value> [XmlIgnore] public Collection<byte> Distances { get { Contract.Ensures(Contract.Result<Collection<byte>>() != null); if (this.distances == null) { this.distances = this.BitDistances; } if (this.distances == null) { throw new InvalidOperationException("List of distances is null."); } return new Collection<byte>(this.distances); } } /// <summary> Gets or sets Tone Level. </summary> /// <value> Property description. </value> public byte ToneLevel { get; set; } /// <summary> Gets measure of rhythmical motion. </summary> /// <value> Property description. </value> public float Mobility => this.Properties.ContainsKey(GenProperty.FormalMobility) ? this.Properties[GenProperty.FormalMobility] : 0f; /// <summary> Gets measure of rhythmical density. </summary> /// <value> Property description. </value> public float Filling => this.Properties.ContainsKey(GenProperty.FormalFilling) ? this.Properties[GenProperty.FormalFilling] : 0f; /// <summary> Gets measure of rhythmical regularity. </summary> /// <value> Property description. </value> public float Variance => this.Properties.ContainsKey(GenProperty.FormalVariance) ? this.Properties[GenProperty.FormalVariance] : 0f; /// <summary> Gets measure of rhythmical beat. </summary> /// <value> Property description. </value> public float Beat => this.Properties.ContainsKey(GenProperty.FormalBeat) ? this.Properties[GenProperty.FormalBeat] : 0f; /// <summary> Gets measure of rhythmical balance. </summary> /// <value> Property description. </value> public float Balance => this.Properties.ContainsKey(GenProperty.FormalBalance) ? this.Properties[GenProperty.FormalBalance] : 0f; /// <summary> Gets measure of rhythmical complexity. </summary> /// <value> Property description. </value> public float Complexity => this.Properties.ContainsKey(GenProperty.FormalComplexity) ? this.Properties[GenProperty.FormalComplexity] : 0f; /// <summary> Gets list of nonzero bits distances. </summary> /// <value> Property description. </value> /// <returns> Returns value.</returns> public string DistanceSchema { get { var s = new StringBuilder(); s.Append("("); for (byte lev = 0; lev < this.Level; lev++) { var p = (lev == this.Level - 1) ? string.Empty : ","; if (lev > 0 && lev < this.Distances.Count) { s.Append(this.Distances[lev] + p); } } s.Append(")"); return s.ToString(); } } #endregion #region Properties - Behavior /// <summary> /// Gets or sets the harmonic behavior. /// </summary> /// <value> /// The harmonic behavior. /// </value> public FormalBehavior FormalBehavior { get; set; } /// <summary> /// Gets or sets the harmonic behavior. /// </summary> /// <value> /// The harmonic behavior. /// </value> public RhythmicBehavior RhythmicBehavior { get; set; } #endregion #region Public methods /// <summary> Makes a deep copy of the BinarySchema object. </summary> /// <returns> Returns object. </returns> public override object Clone() { return new BinarySchema(this.GSystem, this.GetStructuralCode); } /// <summary> Formal position of the given nonzero bit. </summary> /// <param name="level">Requested formal level.</param> /// <returns>Returns value.</returns> public byte PlaceAtLevel(byte level) { if (level >= this.Places.Count) { throw new ArgumentException("Incorrect level"); } return this.Places[level]; } /// <summary> Real position of the given nonzero bit. </summary> /// <param name="level">Requested real level.</param> /// <returns>Returns value.</returns> public short RealPlaceAtLevel(short level) { short p = 0; if (this.Level == 0) { return p; } while (level < 0) { level += this.Level; p -= this.GSystem.Order; } while (level >= this.Level) { level -= this.Level; p += this.GSystem.Order; } var idx = level % this.Level; if (idx < this.Places.Count) { p += this.Places[idx]; } return p; } /// <summary> Distance of bit pair on given Level. </summary> /// <param name="level">Requested level.</param> /// <returns>Returns value.</returns> public byte DistanceAtLevel(byte level) { if (level >= this.Distances.Count) { throw new ArgumentException("Incorrect level"); } return (byte)(level < this.Level ? this.Distances[level] : 0); } /// <summary> /// Range of bit pair on given Level. /// </summary> /// <param name="givenLevel">The given level.</param> /// <returns> /// Returns value. /// </returns> public BitRange RangeAtLevel(byte givenLevel) { if (givenLevel >= this.Places.Count) { throw new ArgumentException("Incorrect level"); } if (givenLevel >= this.Level) { return null; } var order = this.GSystem.Order; var place = this.PlaceAtLevel(givenLevel); var length = this.DistanceAtLevel(givenLevel); if (givenLevel == (byte)(this.Level - 1)) { var diff = (short)(place + length - order); if (diff > 0 && diff < length) { length = (byte)(length - diff); } } var range = new BitRange(order, place, length); return range; } /// <summary> Range of bit pair on given Level - simplified algorithm. </summary> /// <param name="level">Requested level.</param> /// <returns>Returns value.</returns> public BitRange RangeForLevel(byte level) { if (level >= this.Places.Count) { throw new ArgumentException("Incorrect level"); } var range = new BitRange(this.GSystem.Order, this.PlaceAtLevel(level), this.DistanceAtLevel(level)); return range; } /// <summary> Returns frequency ratio of given level. </summary> /// <param name="level">Requested level.</param> /// <value> Given level can exceed modality level. </value> /// <returns> Returns value. </returns> public float RatioForLevel(int level) { Contract.Requires(this.Level != 0); if (level % this.Level >= this.Places.Count) { throw new ArgumentException("Incorrect level"); } var lev = (byte)(level % this.Level); //// if (this.Level <= 0) { return r; } var n = level / this.Level; //// if (this.Order != 0) { //// was r (nonsense) var r = (float)Math.Pow(2, n + ((float)this.PlaceAtLevel(lev) / this.Order)); return r; } /// <summary> Returns index of level containing given bit. </summary> /// <param name="givenBit">Given element/bit.</param> /// <returns> Returns value. </returns> public byte LevelContainingBit(byte givenBit) { for (byte lev = 0; lev < this.Level; lev++) { if (this.RangeForLevel(lev).ContainsBit(givenBit)) { return lev; } } return 0; } #endregion #region String representation /// <summary> List of nonzero bits places. </summary> /// <returns> Returns value.</returns> public string PlaceString() { var s = new StringBuilder(); for (byte lev = 0; lev < this.Level; lev++) { if (lev >= this.Places.Count) { continue; } s.Append(this.Places[lev]); if (lev < this.Level - 1) { s.Append(","); } } return s.ToString(); } /// <summary> String representation of the object. </summary> /// <returns> Returns object. </returns> public override string ToString() { var s = new StringBuilder(base.ToString()); s.Append(" "); s.Append(this.DistanceSchema); return s.ToString(); } #endregion #region Compute properties /// <summary> Evaluate and set Rhythmic properties. /// </summary> public void ComputeRhythmicProperties() { this.FormalBehavior.Variance = this.ComputeVariance(); this.FormalBehavior.Balance = this.ComputeBalance(); this.RhythmicBehavior.Filling = this.ComputeFilling(); this.RhythmicBehavior.Beat = this.ComputeBeat(); this.RhythmicBehavior.Mobility = this.ComputeMobility(); this.RhythmicBehavior.Complexity = this.ComputeComplexity(); } /// <summary> /// Writes the behavior to properties. /// </summary> public override void WriteBehaviorToProperties() { if (this.FormalBehavior != null) { this.Properties[GenProperty.FormalBalance] = this.FormalBehavior.Balance; this.Properties[GenProperty.FormalVariance] = this.FormalBehavior.Variance; this.Properties[GenProperty.FormalEntropy] = this.FormalBehavior.Entropy; } if (this.RhythmicBehavior != null) { this.Properties[GenProperty.FormalFilling] = this.RhythmicBehavior.Filling; this.Properties[GenProperty.FormalBeat] = this.RhythmicBehavior.Beat; this.Properties[GenProperty.FormalMobility] = this.RhythmicBehavior.Mobility; this.Properties[GenProperty.FormalComplexity] = this.RhythmicBehavior.Complexity; } } #endregion #region Evaluation of properties /// <summary> /// Determine and sets the variance property. /// </summary> /// <returns>Returns value.</returns> public float ComputeVariance() { // variational quotient float variance = 0; if (this.Level > 1) { var md = this.MeanDistance(); var value = 0.0F; for (byte e = 0; e < this.Level; e++) { value = value + (float)Math.Pow( Math.Abs(this.DistanceAtLevel(e) - md), 2.0); } if (this.Level > 0 && md >= DefaultValue.AfterZero && md <= DefaultValue.LargeNumber) { //// Math.Abs(md) > 0.00001 variance = (float)Math.Sqrt(value / this.Level) / md * 100f; // Level-1 } } return variance; } /// <summary> /// Determine and sets the complexity. /// </summary> /// <returns>Returns value.</returns> protected float ComputeComplexity() { var complexity = 0f; if (this.Level > 1) { int value = 0, tv = 0; for (byte lev = 0; lev < this.Level; lev++) { var p = this.PlaceAtLevel(lev); if (p <= 0) { continue; } var d = (byte)MathSupport.GreatestCommonDivisor(this.GSystem.Order, p); tv++; if (d < p) { value++; } tv++; if (d == 1) { value++; } } if (tv != 0) { complexity = (float)value / tv * 100.0f; } } return complexity; } /// <summary> /// Determine and sets the complexity. /// </summary> /// <returns>Returns value.</returns> protected float ComputeComplexity2() { //// the algorithm is not very good ?! var complexity = 0f; if (this.Level > 1) { long value = this.GSystem.Order; // 1L; for (byte lev = 0; lev < this.Level; lev++) { long p = this.DistanceAtLevel(lev); if (p > 0) { value = (long)MathSupport.LeastCommonMultiple(value, p); } } var denominator = 2.0f * this.GSystem.Order * this.GSystem.Order; complexity = value / denominator * 100.0f; if (complexity > 100) { complexity = 100.0f; } } return complexity; } /// <summary> /// Mean distance of nonzero bits. /// </summary> /// <returns> /// Returns object. /// </returns> protected float MeanDistance() { //// Contract.Requires(this.GSystem != null); if (this.Level < 1) { return 1.0F; } var value = this.GSystem.Order / (float)this.Level; return value; } /// <summary> /// Determine and sets the mobility property. /// </summary> /// <returns>Returns value.</returns> protected float ComputeMobility() { //// float mobility = this.GSystem.Order != 0 ? (Level / (float)this.GSystem.Order) * 100f : 0; var mobility = (this.ToneLevel / (float)this.Level) * 100f; //// this.GSystem.Order > 0 ? (this.ToneLevel / (float)this.Level) * 100f : 0; return mobility; } /// <summary> /// Determine and sets the filling property. /// </summary> /// <returns>Returns value.</returns> protected float ComputeFilling() { byte sum = 0; var order = this.GSystem.Order; var isPause = this.IsOn(0); for (byte e = 0; e < order; e++) { if (this.IsOn(e)) { isPause = false; } if (!isPause) { sum++; } } var filling = order != 0 ? 100.0f * sum / order : 0; return filling; } /// <summary> /// Determine and sets the side property. /// </summary> /// <returns> /// Returns value. /// </returns> protected float ComputeBalance() { var median = this.GSystem.Median; float left = this.IsOnInRange(0, (byte)(median - 1)); //// Contract.Assume(this.GSystem != null); float right = this.IsOnInRange(median, (byte)(this.GSystem.Order - 1)); var balance = this.Level > 0 ? (DefaultValue.HalfUnit + ((right - left) / this.GSystem.Order)) * 100.0f : 0f; return balance; } /// <summary> /// Determine and sets the beat property. /// </summary> /// <returns>Returns value.</returns> protected float ComputeBeat() { var beat = 0F; if (this.Level > 0) { float v = 0, tv = 0; var order = this.GSystem.Order; for (byte m = 2; m < order; m++) { if (order % m != 0) { continue; } for (byte e = 0; e < order; e++) { if (e == 1 || e % m != 0) { continue; } float dv = m; if (e < this.BitArray.Count && this.BitArray[e]) { //// = this.IsOn(e) (time optimization) v += dv; } tv += dv; } } if (tv >= DefaultValue.AfterZero && tv <= DefaultValue.LargeNumber) { //// Math.Abs(tv) > 0.0001 beat = v / tv * 100.0f; } } return beat; } /// <summary> /// Determine and sets the entropy property. /// </summary> /// <returns>Returns value.</returns> protected float ComputeEntropy() { //// Contract.Assume(this.GSystem != null); var entropy = 0f; if (this.Level > 1) { var value = 0.0f; for (byte lev = 0; lev < this.Level; lev++) { var p = (float)this.DistanceAtLevel(lev) / this.GSystem.Order; if (p > 0) { value = (float)(value + (p * Math.Log(p))); } } var logLevel = (float)Math.Log(this.Level); if (logLevel >= DefaultValue.AfterZero && logLevel <= DefaultValue.LargeNumber) { entropy = -value / logLevel * 100f; } } return entropy; } #endregion } }